/**
 * Copyright (c) 2008 Trent Hoeppner
 * 
 * This file is part of BillCalc.
 * 
 * BillCalc is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * BillCalc is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with BillCalc.  If not, see <http://www.gnu.org/licenses/>.
 **/

package com.trentech.billcalc.gui;

import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import com.trentech.billcalc.Biller;
import com.trentech.billcalc.FinanceManager;
import com.trentech.billcalc.FinanceManagerAdapter;
import com.trentech.billcalc.Resident;

/**
 * BillCalcFrame allows the user to visually see the results and calculations
 * in a GUI.  The user can choose between several views to understand the 
 * calculations better.
 * 
 * @author Trent Hoeppner
 */
public class BillCalcFrame extends JFrame {


    /**
     * Required for serialization.
     */
    private static final long serialVersionUID = -3229432188927505661L;
    
    
    /**
     * The FinanceManager to query for data.
     */
    private FinanceManager manager;
    
    
    /**
     * The view that shows debts in list form.
     */
    private DetailView detailView;


    /**
     * The component that shows the residents and allows the user to select 
     * them.
     */
    private JList residentList;
    
    
    /**
     * The manager for tooltips and label highlighting.
     */
    private UserFriendlinessManager ufManager;
    

    /**
     * Constructor for BillCalcFrame.
     * 
     * @param   manager the FinanceManager that serves as the model.  Cannot 
     *                  be null.
     */
    public BillCalcFrame(FinanceManager manager) {
        super(Messages.getString("BillCalcFrame.mainFrameTitle"));
        
        this.manager = manager;
        ufManager = new UserFriendlinessManager(this);
        
        JPanel contentPane = new JPanel();
        setContentPane(contentPane);
        contentPane.setLayout(new BorderLayout());
        
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        JTabbedPane allPanes = new JTabbedPane();
        contentPane.add(allPanes, BorderLayout.CENTER);
        
        JPanel costPane = new JPanel();
        allPanes.addTab(Messages.getString("BillCalcFrame.costsTab"), costPane);
        costPane.setLayout(new GridLayout(1, 1));
        
        residentList = new JList(manager.getResidents().toArray());
        residentList.addListSelectionListener(new ResidentSelectionListener());
        detailView = new DetailView(true);
        
        manager.addFinanceManagerListener(new FinanceManagerAdapter() {
            public void eventsProcessed() {
                repopulateResidentList();
            }
        });
        
        JSplitPane bigSplit = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, 
                true, residentList, detailView);
        costPane.add(bigSplit);
        
        EventView eventView = new EventView(manager, ufManager);
        allPanes.addTab(Messages.getString("BillCalcFrame.eventsTab"), eventView);
        
        setSize(600, 500);
        setLocation(200, 200);
        setVisible(true);
    }
    
    
    /**
     * Loads an image in this package with the given filename.
     * 
     * @param   imageName   the name of the image to load.  Cannot be null or 
     *                      empty.
     *                      
     * @return  an Icon with the image.  Will not be null.
     */
    static Icon loadImage(String imageName) {
        return new ImageIcon(BillCalcFrame.class.getResource(imageName));
    }
    
    
    /**
     * Resets the data in the resident list component.
     */
    protected void repopulateResidentList() {
    	
        residentList.setListData(manager.getResidents().toArray());
    }


    /**
     * Updates the detail view with the debts that the given Resident has toward
     * all Billers and other Residents.  If the given Resident has moved out, 
     * but some bills are forthcoming, estimates for those bills will also be
     * provided.
     * 
     * @param   resident    the Resident whose details will be shown in the 
     *                      detail view.  Cannot be null.
     */
    private void changeDetailView(Resident resident) {
        List<String> names = new ArrayList<String>();
        List<Integer> owing = new ArrayList<Integer>();
        
        // first Billers
        for (Biller biller : resident.getOwedBillers()) {
            int owed = resident.getAmountOwed(biller).intValue();
            if (owed != 0) {
                names.add(biller.getName());
                owing.add(owed);
            }

            // then Biller estimates
            if (manager.hasEstimatedCosts(resident, biller)) {
                int estimate = manager.calcEstimatedCost(resident, biller).intValue();
                if (estimate != 0) {
                    names.add(Messages.getUnpaidEstimate(biller.getName()));
                    owing.add(estimate);
                }
            }
        }
        
        // then other roommates
        for (Resident otherResident : resident.getOwedResidents()) {
            int owed = manager.getAmountOwed(resident, otherResident);
            if (owed != 0) {
                names.add(otherResident.getName());
                owing.add(owed);
            }
        }
        
        detailView.setValues(Messages.getWhoOwesTheFollowing(resident.getName()), names, owing);
    }
    
    
    /**
     * Updates the detail view with the debts that first has toward second,
     * broken down by Biller.
     * 
     * @param   first   the first Resident.  Cannot be null.
     * @param   second  the second Resident.  Cannot be null.
     */
    private void changeDetailView(Resident first, Resident second) {
        List<String> billerNames = new ArrayList<String>();
        List<Integer> owing = new ArrayList<Integer>();
        
        Map<Biller, BigDecimal> debtsByBiller = first.getDebtsTowardResident(second);
        if (debtsByBiller != null) {
            for (Biller biller : debtsByBiller.keySet()) {
                int debt = debtsByBiller.get(biller).intValue();
                
                if (debt != 0) {
                    billerNames.add(biller.getName());
                    owing.add(debt);
                }
            }
        }
        
        detailView.setValues(Messages.getWhoOwesWho(first.getName(), second.getName()), 
                billerNames, owing);
    }
    
    
    /**
     * Updates the detail view with the total debts that each resident has.
     */
    private void changeDetailView() {
        List<String> residentNames = new ArrayList<String>();
        List<Integer> owing = new ArrayList<Integer>();
        
        for (Resident resident : manager.getResidents()) {
            residentNames.add(resident.getName());
            owing.add(manager.getTotalOwing(resident));
        }
        
        detailView.setValues(Messages.getString("BillCalcFrame.eachOwes"), 
                residentNames, owing);
    }
    

    /**
     * ResidentSelectionListener listens for changes to the selection of 
     * Residents, and updates the view accordingly.
     */
    public class ResidentSelectionListener implements ListSelectionListener {
        
        /**
         * Constructor for ResidentSelectionListener.
         */
        public ResidentSelectionListener() {
            // do nothing
        }
        

        /**
         * {@inheritDoc}
         */
        public void valueChanged(ListSelectionEvent e) {
            JList list = (JList) e.getSource();
            if (list.getSelectedIndex() < 0) {
            	
                changeDetailView();
            }
            else {
            	
                Object[] items = list.getSelectedValues();
                if (items.length == 1) {
                    changeDetailView((Resident) items[0]);
                }
                else {
                    changeDetailView((Resident) items[0], (Resident) items[1]);
                }
            }
        }

    }

}
